use async_trait::async_trait;

pub mod codec;
pub mod ctx;
pub mod ietf;
pub mod ssh;
pub mod ssh_call_home;

pub const DEFAULT_SSH_PORT: u16 = 8300;

pub type Capability = String;
pub type SessionId = u32;
pub type RpcNameType = String;
pub type RpcNsType = String;
pub type MessageIdType = String;
pub type DataTreeType = String;

#[derive(Debug, Clone)]
pub enum NcMsg {
    Rpc(MessageIdType, RpcNsType, RpcNameType, DataTreeType),
    Hello(Option<SessionId>, Box<Vec<Capability>>),
    RpcReply(MessageIdType, RpcReplyType),
    Notifycation(Vec<u8>),
    None,
    NcOp(MessageIdType, NcOpType),
}

impl NcMsg {
    pub fn to_ncxml(&self) -> String {
        match self {
            Self::Hello(session_id, _caps) => {
                format!(r#"
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
     <capabilities>
       <capability>
         urn:ietf:params:netconf:base:1.1
       </capability>
       <capability>
         urn:ietf:params:netconf:capability:startup:1.0
       </capability>
     </capabilities>
     <session-id>{:?}</session-id>
</hello>
                "#, session_id)
            }
            Self::Notifycation(msgs) => {
                String::from_utf8_lossy(msgs).to_string()
            }
            Self::RpcReply(message_id, msg) => {
                match msg {
                    RpcReplyType::Data(data) => {
"".to_string()
                    }
                    RpcReplyType::Err(err) => {
format!(r#"
<rpc-reply message-id="{}"
          xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
     <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
       <rpc-error>
         <error-type>rpc</error-type>
         <error-tag>missing-attribute</error-tag>
         <error-severity>error</error-severity>
         <error-info>
           <bad-attribute>message-id</bad-attribute>
           <bad-element>rpc</bad-element>
         </error-info>
       </rpc-error>
     </rpc-reply>
</rpc-reply>
"#, message_id)
                    }
                    RpcReplyType::Ok => {
format!(r#"
<rpc-reply message-id="{}"
          xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
       <ok/>
</rpc-reply>
"#, message_id)
                    }
                }
            }
            _ => "".to_string()
        }
    }
}

#[derive(Debug, Clone)]
pub enum RpcReplyType {
    Ok,
    Data(DataTreeType),
    Err(RpcError)
}

#[derive(Debug, Clone)]
pub struct RpcError {
    pub r#type: ErrorType,
    pub tag: ErrorTag,
    pub severity: ErrorSeverity,
    pub info: DataTreeType,
}

#[derive(Debug, Clone)]
pub enum ErrorType {
    Transport,
    Rpc,
    Protocol,
    Application,
}

#[derive(Debug, Clone)]
pub enum ErrorTag {
    InUse,
    InvalidValue,
    TooBig,
    MissingAttribute,
    BadAttribute,
    UnknownAttribute,
    MissingElement,
    BadElement,
    UnknownElement,
    UnknownNamespace,
    AccessDenied,
    LockDenied,
    ResourceDenied,
    RollbackFailed,
    DataExists,
    DataMissing,
    OperationNotSupported,
    OperationFailed,
    PartialOperation,
}

#[derive(Debug, Clone)]
pub enum ErrorSeverity{
    Error,
    Warning
}

#[derive(Debug, Clone)]
pub enum NcOpType {
    CloseSession,
    GetConfig(DataTreeType),
    EditConfig,
    CopyConfig,
    DeleteConfig,
    Lock,
    Unlock,
    Get,
    KillSession,
    Commit,
    DiscardChanges,
    CancelCommit,
    Validate,
}

#[async_trait]
pub trait NetconfServerHandler {
    /// 接收消息
    async fn on_message(&mut self, msg: NcMsg) -> NcMsg;

    /// 消息推送
    async fn notify(&mut self, msg: NcMsg);

    /// hello
    async fn on_hello(&mut self, msg: NcMsg) -> NcMsg;
}
